
package com.shiku.utils.encrypt;


import com.shiku.utils.Base64;

import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;


public class RSA {

    public static RsaKeyPair genKeyPair() {

        try {

            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");


            keyPairGen.initialize(1024, new SecureRandom());


            KeyPair keyPair = keyPairGen.generateKeyPair();

            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

            return new RsaKeyPair(publicKey.getEncoded(), convertPkcs8ToPkcs1(privateKey.getEncoded()));

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }


    public static byte[] encrypt(byte[] str, byte[] publicKey) {

        try {

            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey));


            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

            cipher.init(1, pubKey);

            return cipher.doFinal(str);

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }


    public static String encryptBase64(byte[] str, byte[] publicKey) {

        return Base64.encode(encrypt(str, publicKey));

    }


    public static byte[] decrypt(byte[] str, byte[] privateKey) {

        try {

            RSAPrivateKey priKey = parseRsaPrivateKey(privateKey);


            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

            cipher.init(2, priKey);

            return cipher.doFinal(str);

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }


    private static RSAPrivateKey parseRsaPrivateKey(byte[] privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException {

        RSAPrivateKey priKey;

        try {

            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(convertPkcs1ToPkcs8(privateKey)));

        } catch (Exception e) {

            priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKey));

        }

        return priKey;

    }


    private static byte[] convertPkcs8ToPkcs1(byte[] pkcs8Bytes) {

        return Arrays.copyOfRange(pkcs8Bytes, 26, pkcs8Bytes.length);

    }


    private static byte[] convertPkcs1ToPkcs8(byte[] pkcs1Bytes) throws GeneralSecurityException {

        int pkcs1Length = pkcs1Bytes.length;

        int totalLength = pkcs1Length + 22;

        byte[] pkcs8Header = {48, -126, (byte) (totalLength >> 8 & 0xFF), (byte) (totalLength & 0xFF), 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, (byte) (pkcs1Length >> 8 & 0xFF), (byte) (pkcs1Length & 0xFF)};


        return join(pkcs8Header, pkcs1Bytes);

    }


    private static byte[] join(byte[] byteArray1, byte[] byteArray2) {

        byte[] bytes = new byte[byteArray1.length + byteArray2.length];

        System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);

        System.arraycopy(byteArray2, 0, bytes, byteArray1.length, byteArray2.length);

        return bytes;

    }


    public static byte[] decryptFromBase64(String str, byte[] privateKey) {

        return decrypt(Base64.decode(str), privateKey);

    }


    public static byte[] sign(byte[] data, byte[] privateKey) {

        try {

            RSAPrivateKey priKey = parseRsaPrivateKey(privateKey);


            Signature signature = Signature.getInstance("Sha1withRSA");

            signature.initSign(priKey);

            signature.update(data);


            return signature.sign();

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }


    public static byte[] sign(String data, byte[] privateKey) {

        return sign(data.getBytes(), privateKey);

    }


    public static String signBase64(String data, byte[] privateKey) {

        return Base64.encode(sign(data.getBytes(), privateKey));

    }


    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) {

        try {

            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);


            KeyFactory keyFactory = KeyFactory.getInstance("RSA");


            PublicKey pubKey = keyFactory.generatePublic(keySpec);


            Signature signature = Signature.getInstance("Sha1withRSA");

            signature.initVerify(pubKey);

            signature.update(data);


            return signature.verify(sign);

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }


    public static boolean verify(String data, byte[] publicKey, byte[] sign) {

        return verify(data.getBytes(), publicKey, sign);

    }


    public static boolean verifyFromBase64(String data, byte[] publicKey, String sign) {

        return verify(data.getBytes(), publicKey, Base64.decode(sign));

    }


    public static class RsaKeyPair {
        private byte[] publicKey;
        private byte[] privateKey;


        public RsaKeyPair(byte[] publicKey, byte[] privateKey) {

            this.publicKey = publicKey;

            this.privateKey = privateKey;

        }


        public byte[] getPublicKey() {

            return this.publicKey;

        }


        public byte[] getPrivateKey() {

            return this.privateKey;

        }


        public String getPublicKeyBase64() {

            return Base64.encode(this.publicKey);

        }


        public String getPrivateKeyBase64() {

            return Base64.encode(this.privateKey);

        }

    }

}


